package cn.com.anysdk.oss.impl;

import cn.com.anysdk.oss.api.IOssConfig;
import cn.com.anysdk.oss.api.IOssService;
import cn.com.anysdk.oss.api.UploadOptions;
import cn.com.anysdk.oss.exception.OssException;
import com.qcloud.cos.COSClient;
import com.qcloud.cos.ClientConfig;
import com.qcloud.cos.auth.BasicCOSCredentials;
import com.qcloud.cos.auth.COSCredentials;
import com.qcloud.cos.model.DeleteObjectsRequest;
import com.qcloud.cos.model.GeneratePresignedUrlRequest;
import com.qcloud.cos.model.ObjectMetadata;
import com.qcloud.cos.model.PutObjectRequest;
import com.qcloud.cos.region.Region;
import lombok.extern.slf4j.Slf4j;

import java.io.File;
import java.io.InputStream;
import java.net.URL;
import java.util.Date;
import java.util.List;

@Slf4j
public class TencentCloudOssService implements IOssService {

    private final COSClient cosClient;
    private final IOssConfig config;

    public TencentCloudOssService(IOssConfig config) {
        this.config = config;
        COSCredentials cred = new BasicCOSCredentials(config.getAccessKeyId(), config.getAccessKeySecret());
        ClientConfig clientConfig = new ClientConfig(new Region(config.getEndpoint()));
        this.cosClient = new COSClient(cred, clientConfig);
    }

    @Override
    public String uploadFile(String objectKey, File file) {
        log.info("Uploading file with key: {}", objectKey);
        try {
            PutObjectRequest putObjectRequest = new PutObjectRequest(config.getBucketName(), objectKey, file);
            cosClient.putObject(putObjectRequest);
            String url = getFileUrl(objectKey);
            log.info("File uploaded successfully. URL: {}", url);
            return url;
        } catch (Exception e) {
            log.error("Failed to upload file with key: {}", objectKey, e);
            throw new OssException("Upload file failed: " + e.getMessage(), e);
        }
    }

    @Override
    public String uploadFile(String objectKey, InputStream inputStream, long size) {
        log.info("Uploading file with key: {}, size: {}", objectKey, size);
        ObjectMetadata metadata = new ObjectMetadata();
        metadata.setContentLength(size);
        try {
            PutObjectRequest putObjectRequest = new PutObjectRequest(config.getBucketName(), objectKey, inputStream, metadata);
            cosClient.putObject(putObjectRequest);
            String url = getFileUrl(objectKey);
            log.info("File uploaded successfully. URL: {}", url);
            return url;
        } catch (Exception e) {
            log.error("Failed to upload file with key: {}", objectKey, e);
            throw new OssException("Upload file failed: " + e.getMessage(), e);
        }
    }

    @Override
    public String uploadFile(String objectKey, File file, UploadOptions options) {
        log.info("Uploading file with key: {}, options: {}", objectKey, options);
        try {
            ObjectMetadata metadata = createMetadata(options);
            PutObjectRequest putObjectRequest = new PutObjectRequest(config.getBucketName(), objectKey, file);
            putObjectRequest.setMetadata(metadata);
            cosClient.putObject(putObjectRequest);
            String url = getFileUrl(objectKey, options.isPrivate() ? options.getExpireTime() : null);
            log.info("File uploaded successfully. URL: {}", url);
            return url;
        } catch (Exception e) {
            log.error("Failed to upload file with key: {}", objectKey, e);
            throw new OssException("Upload file failed: " + e.getMessage(), e);
        }
    }

    @Override
    public InputStream downloadFile(String objectKey) {
        log.info("Downloading file with key: {}", objectKey);
        try {
            InputStream inputStream = cosClient.getObject(config.getBucketName(), objectKey).getObjectContent();
            log.info("File downloaded successfully with key: {}", objectKey);
            return inputStream;
        } catch (Exception e) {
            log.error("Failed to download file with key: {}", objectKey, e);
            throw new OssException("Download file failed: " + e.getMessage(), e);
        }
    }

    @Override
    public void deleteFile(String objectKey) {
        log.info("Deleting file with key: {}", objectKey);
        try {
            cosClient.deleteObject(config.getBucketName(), objectKey);
            log.info("File deleted successfully with key: {}", objectKey);
        } catch (Exception e) {
            log.error("Failed to delete file with key: {}", objectKey, e);
            throw new OssException("Delete file failed: " + e.getMessage(), e);
        }
    }

    @Override
    public void deleteFiles(List<String> objectKeys) {
        log.info("Deleting files with keys: {}", objectKeys);
        try {
            DeleteObjectsRequest deleteObjectsRequest = new DeleteObjectsRequest(config.getBucketName());
            deleteObjectsRequest.setKeys(objectKeys.stream()
                    .map(DeleteObjectsRequest.KeyVersion::new)
                    .toList());
            cosClient.deleteObjects(deleteObjectsRequest);
            log.info("Files deleted successfully with keys: {}", objectKeys);
        } catch (Exception e) {
            log.error("Failed to delete files with keys: {}", objectKeys, e);
            throw new OssException("Delete files failed: " + e.getMessage(), e);
        }
    }

    @Override
    public String getFileUrl(String objectKey) {
        log.info("Generating file URL for key: {}", objectKey);
        return getFileUrl(objectKey, null);
    }

    @Override
    public String getSignedUrl(String objectKey, long expireSeconds) {
        log.info("Generating signed URL for key: {}, expireSeconds: {}", objectKey, expireSeconds);
        return getFileUrl(objectKey, expireSeconds);
    }

    @Override
    public boolean doesObjectExist(String objectKey) {
        log.info("Checking existence of object with key: {}", objectKey);
        boolean exists = cosClient.doesObjectExist(config.getBucketName(), objectKey);
        log.info("Object with key: {} exists: {}", objectKey, exists);
        return exists;
    }


    @Override
    public IOssConfig getConfig() {
        return this.config;
    }

    @Override
    public boolean validateConfig() {
        return config != null &&
               config.getAccessKeyId() != null && !config.getAccessKeyId().trim().isEmpty() &&
               config.getAccessKeySecret() != null && !config.getAccessKeySecret().trim().isEmpty() &&
               config.getEndpoint() != null && !config.getEndpoint().trim().isEmpty() &&
               config.getBucketName() != null && !config.getBucketName().trim().isEmpty();
    }

    private String getFileUrl(String objectKey, Long expireTime) {
        log.info("Generating file URL for key: {}, expireTime: {}", objectKey, expireTime);
        try {
            if (config.isPrivateMode() || expireTime != null) {
                Date expiration = new Date(System.currentTimeMillis() + (expireTime != null ? expireTime * 1000 : 3600 * 1000));
                GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(config.getBucketName(), objectKey);
                request.setExpiration(expiration);
                URL url = cosClient.generatePresignedUrl(request);
                log.info("Generated signed URL for key: {}", objectKey);
                return url.toString();
            } else {
                String url = String.format("https://%s.%s/%s", config.getBucketName(), config.getEndpoint(), objectKey);
                log.info("Generated public URL for key: {}", objectKey);
                return url;
            }
        } catch (Exception e) {
            log.error("Failed to generate URL for key: {}", objectKey, e);
            throw new OssException("Generate URL failed: " + e.getMessage(), e);
        }
    }

    private ObjectMetadata createMetadata(UploadOptions options) {
        ObjectMetadata metadata = new ObjectMetadata();
        if (options.getContentType() != null) {
            metadata.setContentType(options.getContentType());
        }
        return metadata;
    }

}
